#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "automata.h"

#define DEBUG

int 
main(int argc, char *argv[])
{
    t_table t;
    FILE * fIn;
    int i;
    
    if(argc!=2)
        error(1,"Use: blabla file\n(file must have \"in\" extension)");

    if((fIn = fopen(strcat(argv[1],".in"),"r"))==NULL)
        error(1,"Could not open file. Check file name is correct.");
    
    t = parseFile(fIn);
    
#ifdef DEBUG
    //Display transition matrix
    printf("Printing transition matrix...\n");
    printMatrix(t.transitions);
    
    //Print input chains
    printf("\nPrinting input chains...\n");
    for(i=0;i<t.input.dim;i++)
        printf("%s\n",t.input.chains[i]);
#endif

    system("PAUSE");	
    return 0;
}

t_matrix
buildMatrix(int fil, int col)
{
    int i,j;
    t_matrix out;
    
    out.fils=fil;
    out.cols=col;
    if((out.data = malloc(sizeof(int *)*fil))== NULL)
        error(1,"Could not allocate memory. Shame on you.");
    
    for(i=0;i<fil;i++)
        out.data[i]=malloc(sizeof(t_vector)*col);

    //Fill matrix with -1 which means the trap/error state transition
    for(i=0;i<fil;i++)
        for(j=0;j<col;j++)
        {
            out.data[i][j].vec = malloc(1*sizeof(int));
            out.data[i][j].vec[0]=-1;
            out.data[i][j].dim=1;
        }
            
    return out;
}


void
printMatrix(t_matrix mat)
{
    int i,j,k;
    for(i=0;i<mat.fils;i++)
    {
        for(j=0;j<mat.cols;j++)
        {
            printf("%d",mat.data[i][j].vec[0]);
            for(k=1;k<mat.data[i][j].dim;k++)
                printf("/%d",mat.data[i][j].vec[k]);
            printf("\t");
        }
        printf("\n");
    }
}

/* Parse input - loads Nondeterministic Finite Automata into memory */
t_table 
parseFile(FILE * f)
{
    t_table t;
    t_matrix m;
    t_vector v;
    t_input in;
    char c, * alpha, buf[BUF_SIZE], buf2[BUF_SIZE], * toks, symbol;
    int cantSym, from_state, to_state, symbol_index, i=0;
    void * aux;
        
    //Reading alphabet - 1st line
    if((alpha = malloc(BUF_SIZE))==NULL)
        error(1,"Could not allocate memory. Shame on you.");
    cantSym = getline(alpha, BUF_SIZE, f);
    if((aux = realloc(alpha, cantSym + 1))==NULL)
        error(1,"Could not allocate memory. Shame on you.");
    else
        t.alphabet = aux;

    //2nd line
    getline(buf,BUF_SIZE,f);
    t.states = atoi(buf);
    
    //3rd line
    getline(buf,BUF_SIZE,f);
    t.iniState = atoi(buf);
    
    //4th line
    getline(buf,BUF_SIZE,f);
    strcpy(buf2,buf);
    toks = strtok(buf," ");
    while(toks != NULL)
    {
        i++;
        toks = strtok(NULL," ");
    }
    t.finalStates.dim=i;
    if((t.finalStates.vec = malloc(i * sizeof(int)))==NULL)
        error(1,"Could not allocate memory. Shame on you.");
    i=0;
    toks = strtok(buf2," ");
    while(toks != NULL)
    {
        t.finalStates.vec[i++]=atoi(toks);
        toks = strtok(NULL," ");
    }
    
    //Build transitions matrix
    m = buildMatrix(t.states,cantSym+1); //add extra symbol: lambda
    
    //Rest, until input lines
    while(getline(buf,BUF_SIZE,f))
    {
        from_state = atoi(strtok(buf," "));
        to_state = atoi(strtok(NULL," "));
        if((toks = strtok(NULL," "))==NULL) //Lambda transition
            symbol = '^';
        else
            symbol = *toks;
        if(from_state > t.states-1 || to_state > t.states-1)
            error(1,"Malformed input file - using more states than declared");
        symbol_index = getSymIndex(t.alphabet,symbol);
        addTransition(&m,from_state,symbol_index,to_state);
    }
    
    //Input chains for processing
    if((in.chains = malloc(10 * sizeof(char *)))==NULL)
        error(1,"Could not allocate memory. Shame on you.");
    in.dim = 0;
    while(getline(buf,BUF_SIZE,f))
    {
        in.chains[in.dim]=malloc(strlen(buf)+1);
        strcpy(in.chains[in.dim++],buf);
        
        //Need to allocate more space
        if(in.dim%10 == 0)
        {
            if((aux = realloc(in.chains, ((in.dim/10)+1)*10*sizeof(char *)))==NULL)
                error(1,"Could not allocate memory. Shame on you.");
            else
                in.chains = aux;
        }
    }
    
    t.input = in;
    t.transitions = m;
   
    fclose(f);
    return t;
}

/* Retrieves symbol index from alphabet*/
int
getSymIndex(char * alphabet, char symbol)
{
    int i,len;

    len=strlen(alphabet);

    //If it's a lambda transition, then return last matix column index    
    if(symbol == '^')
        return len;
    
    for(i=0;i<len;i++)
        if(alphabet[i] == symbol)
            break;
    
    return i;
}

/* Adds a transition to the transition's matrix*/
void
addTransition(t_matrix * m, int from_state, int symbol_index, int to_state)
{
    void * aux;
    
    if(m->data[from_state][symbol_index].vec[0] == -1)
    {
        m->data[from_state][symbol_index].vec[0] = to_state;
        return ;
    }

    if((aux = realloc(m->data[from_state][symbol_index].vec,
        (m->data[from_state][symbol_index].dim + 1) * sizeof(int)))==NULL)
        error(1,"Could not allocate memory. Shame on you.");
    else
        m->data[from_state][symbol_index].vec = aux;

    m->data[from_state][symbol_index].vec[m->data[from_state][symbol_index].dim] = to_state;     
    m->data[from_state][symbol_index].dim++;       
}

/* Reads a line, returns its length*/
int
getline(char * line, int max, FILE * f)
{
    int cant;
    
    if(fgets(line, max, f)==NULL)
        return 0;
    else{
        cant = strlen(line)-1;
        if(line[cant]=='\n')
        {
            line[cant]='\0';
            return cant;
        }
        else
            return cant+1;
    }
}

void
error(int critical, char * msg)
{
    fprintf(stderr,"%s\n",msg);
    system("PAUSE");	
    if(critical)
        exit(EXIT_FAILURE);
}
